/*
 * I2cInterface.cpp
 *
 *  Created on: 2021-07-25
 *      Author: vicent-PC
 */

#include "I2cInterface.h"

#include "Custom_Config.h"
#include "c3log.h"

#define MAX_I2C_ADDR 0x7F
#define MAX_REG_ADDR 0xFF

#define ESP_SLAVE_ADDR 0x40

#define WRITE_BIT I2C_MASTER_WRITE /*!< I2C master write */
#define READ_BIT I2C_MASTER_READ   /*!< I2C master read */
#define ACK_CHECK_EN 0x1           /*!< I2C master will check ack from slave*/
#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
#define ACK_VAL 0x0       /*!< I2C ack value */
#define NACK_VAL 0x1      /*!< I2C nack value */

#define LOG_TAG "I2cInterface"

I2cInterface::I2cInterface(uint8_t i2cAddr) { mI2cAddr = i2cAddr; }

I2cInterface::I2cInterface() {
    // TODO Auto-generated constructor stub
    m_i2c_port_id = 0;
}

I2cInterface::~I2cInterface() {
    // TODO Auto-generated destructor stub
}

/**
 * @brief test code to read esp-i2c-slave
 *        We need to fill the buffer of esp slave device, then master can read
 * them out.
 *
 * _______________________________________________________________________________________
 * | start | slave_addr + rd_bit +ack | read n-1 bytes + ack | read 1 byte +
 * nack | stop |
 * --------|--------------------------|----------------------|--------------------|------|
 *
 */
esp_err_t I2cInterface::i2c_probe(i2c_port_t i2c_num, uint8_t slave_addr,
                                  uint16_t *reg_val = nullptr) {
    int ret = ESP_OK;
    uint8_t read_val_high = 0, read_val_low = 0;
    // first: set addr
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (slave_addr << 1) | READ_BIT, ACK_CHECK_DIS);
    i2c_master_write_byte(cmd, 0x00, ACK_CHECK_EN);
    i2c_master_stop(cmd);
    ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS);
    if (ret != 0) {
        LOGE("%s first i2c_master_cmd_begin i2c_num:%d err:%d", __func__,
             i2c_num, ret);
        return ret;
    }
    i2c_cmd_link_delete(cmd);

    // third: read 16bits
    vTaskDelay(20 / portTICK_PERIOD_MS);
    cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (slave_addr << 1) | READ_BIT, ACK_CHECK_EN);
    i2c_master_read_byte(cmd, &read_val_high, I2C_MASTER_ACK);
    i2c_master_read_byte(cmd, &read_val_low, I2C_MASTER_NACK);
    i2c_master_stop(cmd);
    ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS);
    if (ret != 0) {
        LOGE("%s third i2c_master_cmd_begin err:%d", __func__, ret);
    }
    i2c_cmd_link_delete(cmd);
    if (nullptr != reg_val) {
        *reg_val = read_val_high << 8 | read_val_low;
    }
    return ret;
}

/**
 * @brief i2c master initialization
 */
void I2cInterface::i2c_master_init() {
    int ret = ESP_OK;
    i2c_config_t conf;
    conf.mode = I2C_MODE_MASTER;
    conf.sda_io_num = SDA_PIN;
    conf.scl_io_num = SCL_PIN;
    conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
    conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
    conf.master.clk_speed = 400 * 1000;
    conf.clk_flags = I2C_SCLK_SRC_FLAG_FOR_NOMAL;
    LOGI("%s i2c_param_config sda:gpio%d scl:gpio%d", __func__, conf.sda_io_num,
         conf.scl_io_num);

    ret = i2c_param_config(I2C_NUM_0, &conf);
    if (ESP_OK != ret) {
        LOGE("%s i2c_param_config fail", __func__);
    } else {
        LOGI("%s i2c_param_config suc", __func__);
    }

    ret = i2c_driver_install(I2C_NUM_0, I2C_MODE_MASTER, 0, 0, 0);
    if (ESP_OK != ret) {
        LOGE("%s i2c_driver_install fail", __func__);
    } else {
        LOGI("%s i2c_driver_install suc", __func__);
    }
}

int I2cInterface::I2cScannerAddr() {
    int ret = i2c_probe(I2C_NUM_0, mI2cAddr);
    if (ESP_OK == ret) {    // Receive 0 = success (ACK response)
        LOGI("find i2c device: 0x%x", mI2cAddr);
    } else {
        LOGE("not find i2c device: 0x%x", mI2cAddr);
    }
    return ret;
}

void I2cInterface::I2cScanner() {
    LOGI("I2C scanner() Devices Scanning ... (0:detect -1: not detect)");
    int ret = ESP_OK;
    uint8_t suc_count = 0;
    char search_res[128] = {0};
    int cur_pos = 0;
    for (uint8_t i = 0; i <= MAX_I2C_ADDR; i++) {
        ret = i2c_probe(I2C_NUM_0, i);

        if (ESP_OK == ret)  // Receive 0 = success (ACK response)
        {
            LOGI("find i2c device: 0x%x", i);
            suc_count++;
        } else {
            LOGI("not find i2c device: 0x%x", i);
        }

        cur_pos += snprintf(search_res + cur_pos, 10, "0x%02x:%d ", i, ret);
        if (i > 0 && i % 8 == 7) {
            LOGI("%s", search_res);
            cur_pos = 0;
        }
    }
    LOGI("total found: %d devices", suc_count);
}

void I2cInterface::regScanner() {
    LOGI("I2C %s Scanning ...", __func__);
    int ret = ESP_OK;
    int suc_count = 0;
    char search_res[128] = {0};
    int cur_pos = 0;
    uint16_t reg_val = 0;
    for (uint16_t i = 0; i <= MAX_REG_ADDR; i++) {
        reg_val = readRegister16(i);

        if (ESP_OK == ret)  // Receive 0 = success (ACK response)
        {
            // LOGI("red ok: 0x%x", i);
            suc_count++;
        }

        cur_pos += snprintf(search_res + cur_pos, 10, "%04x ", reg_val);
        if (i > 0 && i % 0xf == 0xe && i % 0xf == 0xe) {
            LOGI("0x%02x-0x%02x(0x):%s", i - 0xe, i, search_res);
            cur_pos = 0;
        }
        if (MAX_REG_ADDR == i) {
            LOGI("0x%02x:%s", MAX_REG_ADDR, search_res);
        }
    }
    LOGI("total scan: %d registers", suc_count);
}

void I2cInterface::stressTest(uint8_t i2c_addr, uint16_t loop_cnt) {
    LOGI("I2C stressTest() testing ...");
    int ret = ESP_OK;
    int fail_cnt = 0;
    uint16_t reg_val = 0;
    for (size_t i = 0; i < loop_cnt; i++) {
        /* code */
        ret = i2c_probe(I2C_NUM_0, i2c_addr, &reg_val);
        if (ESP_OK == ret) {    // Receive 0 = success (ACK response)
            // LOGI("i2c device: 0x40 run OK", i);
        } else {
            fail_cnt++;
        }

        if (999 == i % 1000) {
            LOGI("i2c device: 0x%02x run:%d fail_cnt:%d", i2c_addr, i + 1,
                 fail_cnt);
        }
    }

    LOGI("i2c device: 0x%02x run fail_cnt:%d/%ld", i2c_addr, fail_cnt,
         loop_cnt);
}

int16_t I2cInterface::readRegister16(uint8_t reg) {
    int ret = 0;
    uint8_t val_high = 0;
    uint8_t val_low = 0;
    // first: set addr, set reg
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (mI2cAddr << 1) | WRITE_BIT, ACK_CHECK_DIS);
    i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
    i2c_master_stop(cmd);
    ret = i2c_master_cmd_begin(m_i2c_port_id, cmd, 1000 / portTICK_RATE_MS);
    if (ret != 0) {
        LOGI("%s first i2c_master_cmd_begin id:%d err:%d", __func__,
             m_i2c_port_id, ret);
        return -1;
    }
    i2c_cmd_link_delete(cmd);

    // third: read 16bits
    // vTaskDelay(2 / portTICK_PERIOD_MS);
    cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (mI2cAddr << 1) | READ_BIT, ACK_CHECK_EN);
    i2c_master_read_byte(cmd, &val_high, I2C_MASTER_ACK);
    i2c_master_read_byte(cmd, &val_low, I2C_MASTER_NACK);
    i2c_master_stop(cmd);
    ret = i2c_master_cmd_begin(m_i2c_port_id, cmd, 1000 / portTICK_RATE_MS);
    if (ret != 0) {
        LOGI("third i2c_master_cmd_begin err;%d", ret);
        return -1;
    }

    i2c_cmd_link_delete(cmd);
    return val_high << 8 | val_low;
}

int I2cInterface::writeRegister16(uint8_t reg, uint16_t val) {
    int ret = 0;
    uint8_t val_high = val >> 8;
    uint8_t val_low = val & 0xff;
    // first: set addr, reg , write 16bits
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (mI2cAddr << 1) | WRITE_BIT, ACK_CHECK_DIS);
    i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
    i2c_master_write_byte(cmd, val_high, ACK_CHECK_EN);
    i2c_master_write_byte(cmd, val_low, ACK_CHECK_EN);
    i2c_master_stop(cmd);
    ret = i2c_master_cmd_begin(m_i2c_port_id, cmd, 1000 / portTICK_RATE_MS);
    if (ret != 0) {
        LOGE("%s i2c_master_cmd_begin err;%d", __func__, ret);
    }
    i2c_cmd_link_delete(cmd);
    return ret;
}

uint8_t I2cInterface::readRegister8(uint8_t reg) {
    int ret = 0;
    uint8_t val = 0;
    // first: set addr, set reg
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (mI2cAddr << 1) | WRITE_BIT, ACK_CHECK_DIS);
    i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
    i2c_master_stop(cmd);
    ret = i2c_master_cmd_begin(m_i2c_port_id, cmd, 1000 / portTICK_RATE_MS);
    if (ret != 0) {
        LOGI("%s first i2c_master_cmd_begin id:%d err:%d", __func__,
             m_i2c_port_id, ret);
        return ret;
    }
    i2c_cmd_link_delete(cmd);

    // third: read 16bits
    vTaskDelay(2 / portTICK_PERIOD_MS);
    cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (mI2cAddr << 1) | READ_BIT, ACK_CHECK_EN);
    i2c_master_read_byte(cmd, &val, I2C_MASTER_ACK);
    i2c_master_stop(cmd);
    ret = i2c_master_cmd_begin(m_i2c_port_id, cmd, 1000 / portTICK_RATE_MS);
    if (ret != 0) {
        LOGI("third i2c_master_cmd_begin err;%d", ret);
    }
    i2c_cmd_link_delete(cmd);

    return val;
}

int I2cInterface::writeRegister8(uint8_t reg, uint8_t val) {
    int ret = 0;
    // first: set addr, reg , write 16bits
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (mI2cAddr << 1) | WRITE_BIT, ACK_CHECK_DIS);
    i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
    i2c_master_write_byte(cmd, val, ACK_CHECK_EN);
    i2c_master_stop(cmd);
    ret = i2c_master_cmd_begin(m_i2c_port_id, cmd, 1000 / portTICK_RATE_MS);
    if (ret != 0) {
        LOGI("%s third i2c_master_cmd_begin err;%d", __func__, ret);
    }
    i2c_cmd_link_delete(cmd);
    return ret;
}
